package com.pigdogbay.androidutils.utils;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;

import com.pigdogbay.androidutils.utils.StringUtils;

public class WordList {
	private static final int MIN_ANAGRAM_LEN = 3;
	private static final int SUBANAGRAM_DEPTH = 3;
	private int _ResultLimit = 200;
	private volatile boolean _Stop;
	List<String> _WordList;
	char[] _Buffer = new char[64];
	int[] _Hashes;

	/**
	 * Signal to stop any searches
	 */
	public void stop(){
		_Stop = true;
	}
	
	/*
	 * Wordlist must be sorted and all lower case
	 */
	public void SetWordList(List<String> wordList) {
		_WordList = wordList;
		if (wordList != null) {
			createHashes();
		}
	}

	public WordList() {
		_WordList = null;
	}

	public void SetResultLimit(int limit) {
		_ResultLimit = limit;
	}

	public void FindSupergrams(String anagram, WordListCallback callback, int length){
		_Stop=false;
		Supergram supergram = new Supergram(anagram);
		int count = 0;
		for (int i = 0; i < _WordList.size(); i++) {
			if (_Stop){break;}
			String word = _WordList.get(i);
			if (length!=0 && word.length()!=length){
				continue;
			}
			if (supergram.isSupergram(word))
			{
				callback.Update(word);
				count++;
				if (count == _ResultLimit) {
					break;
				}
			}
		}
	}

	/**
	 * Using hashes 35ms Without hashes 377ms
	 * 
	 * @param anagram
	 * @param callback
	 */
	public void FindAnagrams(String anagram, WordListCallback callback) {
		_Stop=false;
		anagram = StringUtils.WordSort(anagram);
		int length = anagram.length();
		int hash = computeHash(anagram);
		for (int i = 0; i < _WordList.size(); i++) {
			if (_Stop){break;}
			if (hash == _Hashes[i]) {
				String word = _WordList.get(i);
				if (word.length() == length) {
					if (anagram.equals(StringUtils.WordSort(word))) {
						callback.Update(word);
					}
				}
			}
		}
	}

	public void FindSubAnagrams(String anagram, WordListCallback callback) {
		_Stop=false;
		List<String> subwords = getSubwords(anagram);
		WordListCallback missingLettersWrapper = new WordListCallback.MissingLettersWrapper(
				anagram, callback);
		WordListCallback filterWrapper = new WordListCallback.FilterWrapper(
				missingLettersWrapper);
		for (String word : subwords) {
			if (_Stop){break;}
			FindAnagrams(word, filterWrapper);
		}

	}

	private List<String> getSubwords(String anagram) {
		int len = anagram.length() - SUBANAGRAM_DEPTH;
		if (len < MIN_ANAGRAM_LEN) {
			len = MIN_ANAGRAM_LEN;
		}
		List<String> subwords = StringUtils.GetSubWords(anagram,len);
		StringUtils.sortByLengthReverse(subwords);
		return subwords;
	}

	public void FindPartialWords(String partialWord, WordListCallback callback) {
		_Stop=false;
		int count = 0;
		int length = partialWord.length();
		Pattern pattern = CreatePattern(partialWord);
		for (String word : _WordList) {
			if (_Stop){break;}
			if (word.length() == length) {
				if (pattern.matcher(word).matches()) {
					callback.Update(word);
					count++;
					if (count == _ResultLimit) {
						break;
					}
				}
			}
		}
	}

	public void FindWildcardWords(String wildcard, WordListCallback callback) {
		_Stop=false;
		int count = 0;
		Pattern pattern = CreatePattern(wildcard);
		for (String word : _WordList) {
			if (_Stop){break;}
			if (pattern.matcher(word).matches()) {
				callback.Update(word);
				count++;
				if (_ResultLimit == count) {
					break;
				}
			}
		}
	}

	private Pattern CreatePattern(String s) {
		s = s.toLowerCase(Locale.US);
		s = s.replace(".", "[a-z]");
		s = s.replace("#", "[a-z]+");
		return Pattern.compile(s, Pattern.CASE_INSENSITIVE);
	}

	public List<String> GetMatchingWords(char[] bannedLetters, int size) {
		_Stop=false;
		ArrayList<String> matches = new ArrayList<String>();
		for (String word : _WordList) {
			if (_Stop){break;}
			if (word.length() == size
					&& StringUtils.DoesNotContainBannedLetters(word,
							bannedLetters)) {
				matches.add(word);
			}
		}
		return matches;
	}

	// / <summary>
	// / Find which letters are not used
	// / Remove all words which are not correct size
	// / Remove all words which contain any of the unused letters
	// / For each word in the wordlist1, find the unused letters
	// / See if any word in wordlist2 matches the unused letters
	// / </summary>
	public void FindMultiwordAnagrams(String word1, String word2,
			WordListCallback callback) {
		_Stop=false;
		int count = 0;
		String sortedLetters = StringUtils.WordSort(word1 + word2);
		char[] bannedLetters = StringUtils.SubtractChars(
				StringUtils.LOWERCASE_ALPHABET, sortedLetters).toCharArray();
		List<String> list1 = GetMatchingWords(bannedLetters, word1.length());
		List<String> list2 = word1.length() == word2.length() ? list1
				: GetMatchingWords(bannedLetters, word2.length());
		List<String> sortedList2 = SortEachWord(list2);
		for (String word : list1) {
			if (_Stop){break;}
			String remainder = StringUtils.SubtractChars(sortedLetters, word);
			for (int i = 0; i < sortedList2.size(); i++) {
				if (_Stop){break;}
				if (sortedList2.get(i).equals(remainder)) {
					callback.Update(word + " " + list2.get(i));
					count++;
					if (count == _ResultLimit) {
						return;
					}
				}
			}
		}
	}

	private List<String> SortEachWord(List<String> unsortedWordsList) {
		ArrayList<String> sortedList = new ArrayList<String>();
		for (String word : unsortedWordsList) {
			sortedList.add(StringUtils.WordSort(word));
		}
		return sortedList;
	}

	private void createHashes() {
		int len = _WordList.size();
		_Hashes = new int[len];
		for (int i = 0; i < len; i++) {
			_Hashes[i] = computeHash(_WordList.get(i));

		}
	}

	/**
	 * Time taken 260ms, 450ms using square
	 * 
	 * @param word
	 * @return
	 */
	private int computeHash(String word) {
		int len = word.length();
		word.getChars(0, len, _Buffer, 0);
		int hash = 0;
		for (int j = 0; j < len; j++) {
			int c = (int) _Buffer[j];
			hash = hash + (c * c);
		}
		return hash;

	}
}
